package com.example.devecohp;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowFactory;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import org.jetbrains.annotations.NotNull;

/**
 * this class is registered in plugin.xml for provide the tool window for this plugin
 */
public class MyToolWindowFactory implements ToolWindowFactory {

    public MyToolWindowFactory() {

    }

    /**
     * to set up the tool window, only called once at the start of the ide
     * @param project the project the toolwindow is for
     * @param toolWindow the tool window object that will be added to the ide
     */
    public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow){
        setUpToolWindow(project,toolWindow);
    }
    public static void setUpToolWindow(@NotNull Project project, @NotNull ToolWindow toolWindow) {
        //initialize the complements that will be added to the tool window
        JLabel defectsLabel;
        JLabel errorsLabel;
        JLabel warnsLabel;
        JLabel infosLabel;
        JTextField filterTextField;
        Splitter secondarySplitter;
        ToolWindowInfo toolWindowInfo=new ToolWindowInfo(project);
        ComboBoxModel<String> comboBoxModel = new DefaultComboBoxModel<>();
        JComboBox<String> languageOption = toolWindowInfo.languageOption;
        languageOption.setModel(comboBoxModel);
        Splitter mainSplitter = new Splitter(true);
        Splitter firstComponentSplitter = new Splitter(false);
        JPanel firstPartPanel = new JPanel();
        defectsLabel = toolWindowInfo.defectsLabel;
        firstPartPanel.add(defectsLabel);
        JPanel secondPartPanel = new JPanel();
        errorsLabel = toolWindowInfo.errorsLabel;
        warnsLabel = toolWindowInfo.warnsLabel;
        infosLabel = toolWindowInfo.infosLabel;
        secondPartPanel.add(errorsLabel);
        secondPartPanel.add(warnsLabel);
        secondPartPanel.add(infosLabel);

        // the filter component
        JPanel thirdPartPanel = new JPanel();
        filterTextField = toolWindowInfo.filterTextField;
        filterTextField.setForeground(Color.GRAY);
        // When loss focus, change the text to "filter by description, ruleId" if the user did not search by anything, and
        //change back when loss focus
        filterTextField.addFocusListener(new FocusListener() {
            @Override
            public void focusGained(FocusEvent e) {
                if (filterTextField.getText().equals("filter by description, ruleId")) {
                    filterTextField.setText("");
                    filterTextField.setForeground(Color.WHITE);
                }
            }
            @Override
            public void focusLost(FocusEvent e) {
                if (filterTextField.getText().isEmpty()) {
                    filterTextField.setForeground(Color.GRAY);
                    filterTextField.setText("filter by description, ruleId");
                }
            }
        });
        //update the upper panel when user type in the search bar
        filterTextField.getDocument().addDocumentListener(new DocumentListener() {
            public void changedUpdate(DocumentEvent e) {
                filterUpper(project);
            }
            public void removeUpdate(DocumentEvent e) {
                filterUpper(project);
            }
            public void insertUpdate(DocumentEvent e) {
                filterUpper(project);
            }
        });
        thirdPartPanel.add(filterTextField);

        Splitter secondAndThirdPartSplitter = new Splitter(false);
        secondAndThirdPartSplitter.setFirstComponent(secondPartPanel);
        secondAndThirdPartSplitter.setSecondComponent(thirdPartPanel);
        secondAndThirdPartSplitter.setProportion(0.5F);
        firstComponentSplitter.setFirstComponent(firstPartPanel);
        firstComponentSplitter.setSecondComponent(secondAndThirdPartSplitter);
        firstComponentSplitter.setProportion(0.1F);
        mainSplitter.setFirstComponent(firstComponentSplitter);

        // initialize the part for defect information
        secondarySplitter = toolWindowInfo.secondarySplitter;
        JPanel upperPanel = new JPanel(new BorderLayout());
        upperPanel.setBackground(new Color(30, 31, 34));
        JBScrollPane scrollPane = toolWindowInfo.scrollPane;
        upperPanel.add(scrollPane, "Center");

        secondarySplitter.setFirstComponent(upperPanel);

        // this part is for the reference documents
        JPanel lowerPanel = new JPanel();
        JLabel lowerLabel = new JLabel("defect description");
        lowerPanel.add(lowerLabel);
        secondarySplitter.setSecondComponent(lowerPanel);
        //update the document panel (lower panel) when update
        languageOption.addItemListener(e -> {
            if(languageOption.getSelectedItem()!=null) {
                Auditor.updateDocumentPanel(project, languageOption.getSelectedItem().toString());
            }
        });

        thirdPartPanel.add(languageOption);


        secondarySplitter.setProportion(0.5F);
        mainSplitter.setSecondComponent(secondarySplitter);
        mainSplitter.setProportion(0.1F);
        ContentFactory contentFactory = ContentFactory.getInstance();
        Content content = contentFactory.createContent(mainSplitter, "", false);
        toolWindow.getContentManager().addContent(content);
        PageManager.getInstance(project).setToolWindowInfo(toolWindowInfo);
        secondPartPanel.add(toolWindowInfo.includeError);
        toolWindowInfo.includeError.setSelected(true);

        secondPartPanel.add(errorsLabel);
        secondPartPanel.add(toolWindowInfo.includeWarning);
        toolWindowInfo.includeWarning.setSelected(true);
        secondPartPanel.add(warnsLabel);
        secondPartPanel.add(toolWindowInfo.includeInfo);
        toolWindowInfo.includeInfo.setSelected(true);
        secondPartPanel.add(infosLabel);
        //update the upper panel when user change what to include in the result

        toolWindowInfo.includeInfo.addItemListener(e -> filterUpper(project));
        toolWindowInfo.includeError.addItemListener(e -> filterUpper(project));
        toolWindowInfo.includeWarning.addItemListener(e -> filterUpper(project));
    }

    /**
     * update the tool window after user change a filter setting
     * @param project the project of the filters
     */
    public static void filterUpper(Project project) {
        // update the defect information parts if some changes in the filter
        PageManager pageManager = PageManager.getInstance(project);
        PageInfo pageInfo = pageManager.getSelected();
        if(pageInfo==null){
            return;
        }
        ToolWindowInfo toolWindowInfo= pageManager.getToolWindowInfo();
        String userInput = toolWindowInfo.filterTextField.getText();

        PageManager.getInstance(project).filterAllRules(userInput);
    }

    /**
     * update the labels and panel after the selected page info is updated
     * @param project the project the user is working on
     */
    public static void updateUpperPanel(Project project){
        PageManager pageManager = PageManager.getInstance(project);
        updateUpperPanel(pageManager);
    }

    /**
     * update the labels and panel after the selected page info is updated
     * @param pageManager the page manager for the project the user is working on
     */
    public static void updateUpperPanel(PageManager pageManager) {
        // update the defects number, error number, warning number and info number
        ToolWindowInfo toolWindowInfo= pageManager.getToolWindowInfo();
        PageInfo selected = pageManager.getSelected();
        if(selected==null){
            return;
        }
        pageManager.replaceWithPage(selected);
        toolWindowInfo.defectsLabel.setText("Defects (" + pageManager.getDefects() + ")");
        toolWindowInfo.defectsLabel.revalidate();
        toolWindowInfo.defectsLabel.repaint();
        toolWindowInfo.warnsLabel.setText("Warns (" + pageManager.getWarnings() + ")");
        toolWindowInfo.warnsLabel.revalidate();
        toolWindowInfo.warnsLabel.repaint();
        toolWindowInfo.errorsLabel.setText("Errors (" + pageManager.getErrors() + ")");
        toolWindowInfo.errorsLabel.revalidate();
        toolWindowInfo.errorsLabel.repaint();
        toolWindowInfo.infosLabel.setText("Infos (" + pageManager.getInfos() + ")");
        toolWindowInfo.infosLabel.revalidate();
        toolWindowInfo.infosLabel.repaint();
        selected.updateLineMarker();
    }

    /**
     * Sort the list of rule message according to the line it happens
     * @param auditList the list of rule message
     */
    public static void sortAuditList(List<String> auditList ){
        auditList.sort((o1, o2) -> {
            //find the line number of the two
            Pattern squareBracketsPattern = Pattern.compile("\\[(\\d+)");
            int index1;
            int index2;
            Matcher squareBracketsMatcher1 = squareBracketsPattern.matcher(o1);
            if(squareBracketsMatcher1.find()){
                index1=Integer.parseInt(squareBracketsMatcher1.group(1));
            }else{
                return 0;
            }

            Matcher squareBracketsMatcher2 = squareBracketsPattern.matcher(o2);
            if(squareBracketsMatcher2.find()){
                index2=Integer.parseInt(squareBracketsMatcher2.group(1));
            }else{
                return 0;
            }
            return index1-index2;
        });
    }
}
